# -*- coding: utf-8 -*-

from Ui_data_comparison import Ui_MainWindow
from splashscreen import new_splashscreen
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSlot, Qt
from PyQt5.QtGui import QBrush, QColor, QPixmap
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QMessageBox, QWidget, QSplashScreen
from oracle import class_oracle
from csv_data import csv_data
import sys
import os
import csv
import time
import traceback
import logging
logging.basicConfig(level=logging.DEBUG, format=" [%(asctime)s] - [%(levelname)s] - [%(message)s]")
# logging.basicConfig(filename="runlog.log", level=logging.DEBUG, format=" [%(asctime)s] - [%(levelname)s] - [%(message)s]")
# logging.disable(logging.DEBUG)

class data_comparison(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(data_comparison, self).__init__(parent)
        self.setupUi(self)
        self.tabWidget.removeTab(0) # 移除前两个tab页，由于移除时tab的index（int）会发生变化，所以第二个tab的index从1变为0
        self.tabWidget.removeTab(0)
        try:
            self.data_load() # 按照Data_Csv生成tab控件
        # logging.debug(self.tabWidget.findChildren(QtWidgets.QTableWidget, name="tableWidget_stkcode", options=Qt.FindChildrenRecursively))
        # 根据对象的Objectname返回对象。参数1：被找的对象的类型、参数2：对象的Objectname、参数3：查找方式Qt.FindChildrenRecursively->递归
        # self.tabWidget.findChildren(QtWidgets.QTableWidget, name="tableWidget_stkcode", options=Qt.FindChildrenRecursively)[0].setColumnCount(10)
        except:
            logging.error(traceback.format_exc())

    # 加载data_csv目录下的csv表数据
    def data_load(self):
        try:
            if os.path.exists(os.path.join(os.path.abspath("."), "Data_Csv")): # 判断是否存在Data_Csv文件
                # 获取Data_Csv所有csv文件的地址
                list_csv_path = return_all_csv_path()
                logging.debug("csv数据地址，list_csv_path:{0}".format(list_csv_path))
                for i, file_path in enumerate(list_csv_path, 1):
                    self.instance_csv_data = csv_data(path=file_path)
                    logging.debug("按照csv表名文件生成tab，正在处理第{0}个表,{1}".format(i, self.instance_csv_data.return_table_name()))
                    # 按照csv表名文件生成tab
                    self.add_tab(tab_name=self.instance_csv_data.return_table_name())
            else:
                QMessageBox.warning(self, '警告', '当前路径下未找到Data_Csv目录，请将要比对的数据放置在当前目录的Data_Csv文件下。', QMessageBox.Ok)
        except:
            logging.error("加载data_csv目录下的csv表数据失败" + traceback.format_exc())

    # 按照csv表名文件生成tab
    def add_tab(self, tab_name):
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab_"+tab_name)
        self.tabWidget.addTab(self.tab, "")
        self.gridLayout = QtWidgets.QGridLayout(self.tab)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout_"+tab_name)
        self.tableWidget = QtWidgets.QTableWidget(self.tab)
        self.tableWidget.setObjectName("tableWidget_"+tab_name)
        # 加载tableWidget数据
        self.add_tableWidget()
        self.gridLayout.addWidget(self.tableWidget, 0, 0, 1, 1)
        _translate = QtCore.QCoreApplication.translate
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", tab_name))
        # 这里使用全局变量存储 tableWidget的objectname 和 table_user table_name，方便后续调用
        Global_Variables.G_tableWidget_ObjectName.append("tableWidget_" + tab_name)  # 在这里记录tableWidget的ObjiectName，用于后续找到所有的tableWidget
        Global_Variables.G_dict_tableWidget_ObjectName_table_user["tableWidget_" + tab_name] = self.instance_csv_data.return_table_user()
        Global_Variables.G_dict_tableWidget_ObjectName_table_name["tableWidget_" + tab_name] = self.instance_csv_data.return_table_name()

    # 加载tableWidget数据
    def add_tableWidget(self):
        row_num, colmun_num = self.instance_csv_data.return_table_row(), self.instance_csv_data.return_table_column()
        logging.debug("表行{0},列{1}".format(row_num, colmun_num))
        if row_num > 0 and colmun_num > 0: # 如果csv有数据才处理
            self.tableWidget.setColumnCount(colmun_num+2) # 额外加上“操作”、“比较结果”两列 ["操作", "比较结果"]
            self.tableWidget.setRowCount(row_num) # 减去csv的第一行 第一行为tablewidget的表头 setHorizontalHeaderLabels设置
            list_table_data = self.instance_csv_data.return_table_data() # 返回tableWidget对象的字段 索引字段、非索引字段 所在列号 list
            list_idx_table_field, list_no_idx_table_field, list_all_table_field = self.instance_csv_data.return_table_field()
            # ——————————————————————————表头加载————————————————————————————————
            if list_table_data:  # 如果csv有数据则获取表头
                list_customer_field = ["操作"] # 额外加上“操作”、“比较结果”两列
                list_customer_field.extend(list_all_table_field)
                list_customer_field.extend(["比较结果"])
                self.tableWidget.setHorizontalHeaderLabels(list_customer_field) # 设置表头
            # ——————————————————————————表数据加载——————————————————————————————
            v_table_column_count = self.tableWidget.columnCount()  # 获取到tableWidget的列数，即["比较结果"]列号
            # 遍历csv数据生成 item
            for i in range(row_num):
                self.tableWidget.setRowHeight(i, 20)  # 设置行高
                item_value = ""
                item = QTableWidgetItem(item_value)
                self.tableWidget.setItem(i, v_table_column_count-1, item) # 先设置["比较结果"]列为空
                # 在tableWidget第一列添加按钮
                self.tableWidget.setCellWidget(i, 0, self.tableWidget_add_button(row=i, tableObjName=self.tableWidget.objectName())) # 生成button传入当前行号、tablewidget的ObjectName
                for j in range(colmun_num):
                    try:
                        item_value = list_table_data[i][j]
                    except:
                        item_value = " "
                    item = QTableWidgetItem(item_value)
                    self.tableWidget.setItem(i, j+1, item) # j+1 在tableWidget预留第1列["操作"]
                    self.tableWidget.item(i, j+1).setToolTip(item_value) # 给item设置tooltip
            table_user = self.instance_csv_data.return_table_user() # csv表用户
            table_name = self.instance_csv_data.return_table_name() # csv表名
            # 创建触发器
            self.add_trigger(table_user=table_user, table_name=table_name, list_no_idx_table_field=list_no_idx_table_field)
            # 禁用触发器
            self.diable_triiger(p_table_user=table_user ,p_table_name=table_name)



    # 在tableWidget第一行添加按钮
    def tableWidget_add_button(self, row, tableObjName):
        """
        :param row: 行号
        :param tableObjName: 当前tableWidget的对象名 
        :return: 要在单元格添加的widget
        """
        widget_button = QWidget()
        # 比较按钮
        compareBtn = QtWidgets.QPushButton("比较")
        compareBtn.setStyleSheet(''' text-align : center;
                                     background-color : DarkSeaGreen;
                                     height : 30px;
                                     border-style: outset;
                                     font : 13px  ''') # 设置按钮显示风格
        # 你可能使用一个槽处理多个组件的信号，有时要传递额外的信息。一种方法是使用lambda表达式
        compareBtn.clicked.connect(lambda:self.single_row_compare(tableObjName=tableObjName, row=row)) # 没看懂？这个lamdba是什么意思？？？ "lamdba:"创建一个匿名函数
        # 删除按钮
        deleteBtn = QtWidgets.QPushButton("删除")
        deleteBtn.setStyleSheet(''' text-align : center;
                                    background-color : NavajoWhite;
                                    height : 30px;
                                    border-style: outset;
                                    font : 13px  ''') # 设置按钮显示风格
        # widget上布局
        hLayout = QtWidgets.QHBoxLayout()
        hLayout.addWidget(compareBtn)
        hLayout.addWidget(deleteBtn)
        hLayout.setContentsMargins(2, 2, 2, 2) # 设置 widgets 在layout左上右下的边距，使其在tableWidget显示更合适
        widget_button.setLayout(hLayout)
        return widget_button

    # 创建触发器
    def add_trigger(self, table_user, table_name, list_no_idx_table_field):
        """
        :param table_user: 
        :param table_name: 
        :param list_no_idx_table_field: 
        :return: 
        """
        # 创建触发器
        try:
            self.instance_oracle = class_oracle(user='*', password='*', ip='*', port='1521', server='*')
        except:
            logging.error("class_oracle实例化失败，SqlPlus连接失败。\n详细信息:" + traceback.format_exc())
        # 创建对应csv表中触发器
        try:
            self.instance_oracle.create_triggere(table_user=table_user, table_name=table_name, list_no_idx_table_field=list_no_idx_table_field)
        except:
            logging.error("instance_oracle.create_triggere 创建表{0}触发器失败。\n详细信息:" + traceback.format_exc().format(table_name))

    # 禁用触发器
    def diable_triiger(self, p_table_user, p_table_name):
        # 实例化
        try:
            self.instance_oracle = class_oracle(user      = '*',
                                                password  = '*',
                                                ip        = '*',
                                                port      = '1521',
                                                server    = '*')
        except:
            logging.error("class_oracle实例化失败，SqlPlus连接失败。\n详细信息:" + traceback.format_exc())
        # 禁用触发器
        try:
            self.instance_oracle.disable_trigger(p_table_user = p_table_user,
                                                 p_table_name = p_table_name)
        except:
            logging.error("禁用触发器执行失败。\n详细信息:" + traceback.format_exc())

    # 启用触发器
    def enable_triiger(self, p_table_user, p_table_name):
        # 实例化
        try:
            self.instance_oracle = class_oracle(user='*',
                                                password='*',
                                                ip='*',
                                                port='1521',
                                                server='*')
        except:
            logging.error("class_oracle实例化失败，SqlPlus连接失败。\n详细信息:" + traceback.format_exc())
        # 启用触发器
        try:
            self.instance_oracle.enable_trigger(p_table_user=p_table_user,
                                                p_table_name=p_table_name)
        except:
            logging.error("启用触发器执行失败。\n详细信息:" + traceback.format_exc())

    # 返回tableWidget对象的字段 索引字段、非索引字段 所在列号 list
    def return_tableWidget_field_column(self):
        """
        :return: <1>索引字段 <2>非索引字段 返回tableWidget对象的字段 索引字段、非索引字段 所在列号 list
        """
        list_idx_field_column = []
        list_no_idx_field_column = []
        v_column = self.tableWidget.columnCount()
        if v_column > 0: # 有数据才获取字段
            for j in range(1, v_column-1): # 从第三列开始 不获取["操作"]第一列, ["比较结果"]最后一列，两列
                v_field = self.tableWidget.horizontalHeaderItem(j).data(0) # data(0) 可以获取对象tableWidgetItem的Value
                if v_field[0] == "[" and v_field[-1] == "]": # 索引字段
                    list_idx_field_column.append(j) # 记录列号
                else:
                    list_no_idx_field_column.append(j) # 记录列号
        # logging.debug("*************list_idx_field_column {0}************list_no_idx_field_column {1}".format(list_idx_field_column, list_no_idx_field_column))
        return list_idx_field_column, list_no_idx_field_column # 索引字段列号 list, 非索引字段列号 list


# --------------------------------------槽信号--------------------------------------#
    # 比对
    @pyqtSlot()
    def on_pushButton_clicked(self):
        for tableWidget_ObjectName in Global_Variables.G_tableWidget_ObjectName:
            # 这里循环遍历
            # 获取指定ObjectName的tableWidget对象
            # 根据对象的Objectname返回对象 list。参数1：被找的对象的类型、参数2：对象的Objectname、参数3：查找方式Qt.FindChildrenRecursively->表示递归
            # tableWidget_ObjectName = "tableWidget_exchangetime"
            tableWidget_table_user = Global_Variables.G_dict_tableWidget_ObjectName_table_user[tableWidget_ObjectName]  # 根据key 取出 value
            tableWidget_table_name = Global_Variables.G_dict_tableWidget_ObjectName_table_name[tableWidget_ObjectName]  # 根据key 取出 value
            # 启用触发器
            try:
                self.enable_triiger(p_table_name=tableWidget_table_name, p_table_user=tableWidget_table_user)
            except:
                logging.error("启用触发器失败。详细信息:\n" + traceback.format_exc())
            try:
                self.tableWidget = self.tabWidget.findChildren(QtWidgets.QTableWidget, name=tableWidget_ObjectName, options=Qt.FindChildrenRecursively)[0]
                # 得到tableWidget的索引列和非索引列号 list
                list_idx_field_column, list_no_idx_field_column = self.return_tableWidget_field_column()
                logging.debug("索引列:{0}，非索引列:{1}".format(list_idx_field_column, list_no_idx_field_column))
                v_row = self.tableWidget.rowCount() # table 行数
                dict_idx_field = {}
                for i in range(v_row): # 行遍历
                    for z in list_idx_field_column: # 索引列遍历
                        dict_idx_field[self.tableWidget.horizontalHeaderItem(z).data(0)[1:-1]] = self.tableWidget.item(i, z).data(0) # 生成字典 {字段:值} [1:-1]为去掉索引字段的"[]" 获取对应列字段名 data(0) 可以获取对象tableWidgetItem的Value
                    for j in list_no_idx_field_column: # 非索引列遍历
                        dict_no_idx_field = {self.tableWidget.horizontalHeaderItem(j).data(0): self.tableWidget.item(i, j).data(0)} # 生成字典 {字段:值} 获取对应列字段名 data(0) 可以获取对象tableWidgetItem的Value
                        logging.debug("正在校验行{0}列{1},列与值为:{2}".format(i, j, dict_no_idx_field))
                        logging.debug("索引列与值:{0}".format(dict_idx_field))
                        logging.debug("表用户:{0},表名{1}".format(tableWidget_table_user, tableWidget_table_name))
                        # 创建触发器
                        try:
                            self.instance_oracle = class_oracle(user='*', password='*', ip='*', port='1521', server='*')
                        except:
                            logging.error("class_oracle实例化失败，SqlPlus连接失败。\n详细信息:" + traceback.format_exc())
                        # 执行对比
                        # 错误号0-比对成功、1-未找到、2-比对失败、3-执行语句失败
                        v_error_no, v_error_info = self.instance_oracle.exec_update(table_user=tableWidget_table_user,
                                                                                    table_name=tableWidget_table_name,
                                                                                    no_idx_field_value=dict_no_idx_field,
                                                                                    idx_field_value=dict_idx_field)
                        logging.debug("应答错误号{0}，应答信息{1}".format(v_error_no, v_error_info))
                        # 处理应答回来的数据
                        self.handle_return_info(row=i, column=j, return_no=v_error_no, return_info=v_error_info)
                        if v_error_no == 1: # 如果未根据索引匹配到数据，则直接将该行所有要比对的数据置灰
                            break
                self.tableWidget.resizeColumnToContents(self.tableWidget.columnCount()-1) # 设置最后一列["比较结果"]列宽自适应
            except:
                logging.error("执行校验失败。详细信息:\n" + traceback.format_exc())
            # 禁用触发器
            try:
                self.diable_triiger(p_table_name=tableWidget_table_name, p_table_user=tableWidget_table_user)
            except:
                logging.error("关闭触发器失败。详细信息:\n" + traceback.format_exc())

    # 单行触发槽函数
    def single_row_compare(self, tableObjName, row):
        """
        :param tableObjName: 点击按钮所在的tableWidget的对象名
        :param row: 点击按钮的行号
        :return: 
        """
        # 根据tableWidget的ObjectName 取出 表用户和表名
        tableWidget_table_user = Global_Variables.G_dict_tableWidget_ObjectName_table_user[tableObjName]  # 根据key 取出 value
        tableWidget_table_name = Global_Variables.G_dict_tableWidget_ObjectName_table_name[tableObjName]  # 根据key 取出 value
        # 启用触发器
        try:
            self.enable_triiger(p_table_name=tableWidget_table_name, p_table_user=tableWidget_table_user)
        except:
            logging.error("启用触发器失败。详细信息:\n" + traceback.format_exc())
        try:
            # 获取指定ObjectName的tableWidget对象
            # 根据对象的Objectname返回对象 list。参数1：被找的对象的类型、参数2：对象的Objectname、参数3：查找方式Qt.FindChildrenRecursively->表示递归
            self.tableWidget = self.tabWidget.findChildren(QtWidgets.QTableWidget, name=tableObjName,options=Qt.FindChildrenRecursively)[0]
            # 得到tableWidget的索引列和非索引列号 list
            list_idx_field_column, list_no_idx_field_column = self.return_tableWidget_field_column()
            logging.debug("索引列:{0}，非索引列:{1}".format(list_idx_field_column, list_no_idx_field_column))
            i = row # 行号
            dict_idx_field = {}
            for z in list_idx_field_column:  # 索引列遍历
                # dict_idx_field = {self.tableWidget.horizontalHeaderItem(z).data(0)[1:-1]: self.tableWidget.item(i, z).data(0)}  # 生成字典 {字段:值} [1:-1]为去掉索引字段的"[]" 获取对应列字段名 data(0) 可以获取对象tableWidgetItem的Value
                dict_idx_field[self.tableWidget.horizontalHeaderItem(z).data(0)[1:-1]] = self.tableWidget.item(i, z).data(0)
            logging.debug("dict_idx_field{0}".format(dict_idx_field))
            for j in list_no_idx_field_column:  # 非索引列遍历
                dict_no_idx_field = {self.tableWidget.horizontalHeaderItem(j).data(0): self.tableWidget.item(i, j).data(0)} # 生成字典 {字段:值} 获取对应列字段名 data(0) 可以获取对象tableWidgetItem的Value
                logging.debug("正在校验行{0}列{1},列与值为:{2}".format(i, j, dict_no_idx_field))
                logging.debug("索引列与值:{0}".format(dict_idx_field))
                logging.debug("表用户:{0},表名{1}".format(tableWidget_table_user, tableWidget_table_name))
                # 创建触发器
                try:
                    self.instance_oracle = class_oracle(user='*', password='*', ip='*',
                                                        port='1521', server='*')
                except:
                    logging.error("class_oracle实例化失败，SqlPlus连接失败。\n详细信息:" + traceback.format_exc())
                # 执行对比
                # 错误号0-比对成功、1-未找到、2-比对失败、3-执行语句失败
                v_error_no, v_error_info = self.instance_oracle.exec_update(table_user=tableWidget_table_user,
                                                                            table_name=tableWidget_table_name,
                                                                            no_idx_field_value=dict_no_idx_field,
                                                                            idx_field_value=dict_idx_field)
                logging.debug("应答错误号{0}，应答信息{1}".format(v_error_no, v_error_info))
                # 处理应答回来的数据
                self.handle_return_info(row=i, column=j, return_no=v_error_no, return_info=v_error_info)
            self.tableWidget.resizeColumnToContents(self.tableWidget.columnCount() - 1)  # 设置最后一列["比较结果"]列宽自适应
        except:
            logging.error("单行执行比对失败。详细信息:\n"+traceback.format_exc())
        # 禁用触发器
        try:
            self.diable_triiger(p_table_name=tableWidget_table_name, p_table_user=tableWidget_table_user)
        except:
            logging.error("关闭触发器失败。详细信息:\n"+traceback.format_exc())


# --------------------------------------槽信号--------------------------------------#

    # 处理应答回来的数据
    def handle_return_info(self, row, column, return_no, return_info):
        """
        :param row: item 行数
        :param column: item 列数
        :param return_no: 比对应答的错误号 错误号 0-比对成功、1-未找到、2-比对失败、3-执行语句失败
        :param return_info: 比对应答的信息
        :return: 无返回
        """
        v_table_column_count = self.tableWidget.columnCount() # 获取到tableWidget的列数，即["比较结果"]列号
        if return_no == 0:
            self.tableWidget.item(row, column).setBackground(QBrush(QColor(84, 255, 159)))  # RGB设置背景色为 SeaGreen1
        elif return_no == 1:
            for j in range(column, v_table_column_count - 1):# 如果未根据索引匹配到数据，则直接将该行所有要比对的数据置灰
                self.tableWidget.item(row, j).setBackground(QBrush(QColor(190, 190, 190)))  # RGB设置背景色为 Grey
            self.tableWidget.item(row, v_table_column_count - 1).setText(return_info) # 比较结果只显示一次
            self.tableWidget.item(row, v_table_column_count - 1).setToolTip(return_info)  # 给item设置tooltip
        elif return_no == 2:
            item_vaule = self.tableWidget.item(row, v_table_column_count-1).text()
            item_vaule = item_vaule + return_info # 比较结果递增记录
            self.tableWidget.item(row, v_table_column_count - 1).setText(item_vaule)  # 比较结果只多条累加
            self.tableWidget.item(row, v_table_column_count - 1).setToolTip(item_vaule)  # 给item设置tooltip
            self.tableWidget.item(row, column).setBackground(QBrush(QColor(255, 106, 106)))  # RGB设置背景色为 IndianRed1
        elif return_no == 3:
            self.tableWidget.item(row, column).setBackground(QBrush(QColor(255, 0, 255)))  # RGB设置背景色为 Magenta1
            self.tableWidget.item(row, v_table_column_count - 1).setText(return_info)  # 比较结果只显示一次
            self.tableWidget.item(row, v_table_column_count - 1).setToolTip(return_info)  # 给item设置tooltip

# --------------------------------------外部函数-----------------------------------#
# 获取Data_Csv所有csv文件的地址
def return_all_csv_path():
    v_count = 0
    list_csv_path = []
    for root, dirs, files in os.walk(os.path.join(os.path.abspath("."), "Data_Csv")):
        v_count += 1
        # root 返回目录 str
        # dirs 返回文件夹 list
        # files 返回文件名 list
        logging.debug("root:{0},dirs:{1},files:{2}".format(root, dirs, files))
        if v_count == 1: # 只获取当前目录下
            for v_csv_file in files:
                if v_csv_file.split(".")[-1] == "csv":
                    list_csv_path.append(os.path.join(os.path.abspath("."), "Data_Csv", v_csv_file))
                    logging.debug("路径{0}".format(list_csv_path))
                else:
                    continue
        else:
            continue
    return list_csv_path

class Global_Variables:
    G_tableWidget_ObjectName = [] # 所有tableWidget的ObjectName
    G_dict_tableWidget_ObjectName_table_user = {} # dict 所有tableWidget的ObjectName:table_user
    G_dict_tableWidget_ObjectName_table_name = {} # dict 所有tableWidget的ObjectName:table_name



if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    splash = new_splashscreen()
    splash.show() # 显示启动界面
    splash.new_showMessage()  # 启动界面显示进度条和提示文字
    app.processEvents() # 处理主进程事件

    data_comparison = data_comparison()
    data_comparison.show()

    splash.finish(data_comparison) # 主窗体对象初始化完成后，结束启动画面。

    sys.exit(app.exec_())